package zdream.pmfield.stage.buff.foe;

import java.util.function.Function;

import zdream.pmfield.data.foe.VStat;
import zdream.pmfield.stage.buff.StageBuff;
import zdream.pmfield.stage.director.Roll;
import zdream.pmfield.stage.director.HookStore;
import zdream.pmfield.stage.director.HookType;
import zdream.pmfield.stage.director.Hooks;
import zdream.pmfield.stage.message.RollReportMessage;
import zdream.pmfield.stage.message.FoeRemoveBuffMessage;
import zdream.pmfield.stage.object.FoeActor;

/**
 * 这里没有 Foe 只能有一个异常状态的限制. {@link FoeActor} 可以同时烧伤和麻痹
 * 但是, 大多数效果都是可以随时间自然恢复的, 包括烧伤和麻痹.
 *
 * @author Zdream
 * @date 2023-11-21
 * @since 0.0.1
 */
public abstract class FoeStageBuff extends StageBuff<FoeActor> {
	public FoeStageBuff(FoeActor target) {
		super(target);
	}

	/**
	 * 在某个 hook 触发的时候调用什么代码.
	 * @return 手动清理掉这个 hook 的方法.
	 *   当然, 这个 hook 也会在 buff 删除之后自动清理掉
	 */
	protected HookStore.HookHandlerBuilder onHook(HookType type) {
		return target.registerHook()
				.withType(type)
				.withOwner(this)
				.withCleaner(cleaner -> onClean(any -> cleaner.get()));
	}

	/**
	 * 触发于 {@link HookType#BEFORE_FOE_RECEIVE_BUFF}, 将阻止相同 {@link #getCategory()} 的 buff
	 */
	protected void preventSameBuffBeforeFoeReceiveBuff() {
		onHook(HookType.BEFORE_FOE_RECEIVE_BUFF)
				.handle(this::preventSameBuff)
				.buildAndSave();
	}

	private void preventSameBuff(Hooks.HookContext rawContext) {
		Hooks.BeforeFoeReceiveBuffContext context = rawContext.as();
		if (context.otherBuff.is(getCategory())) {
			context.abort();
		}
	}

	/**
	 * 适用于随时间慢慢治愈或消失的 Buff.
	 */
	public class DurationBuffSpan extends BuffSpan {
		float startTime;
		float minDuration = 3;
		Function<Float, Float> checkRateFunction = duration -> duration * 20 - 100;

		@Override
		public boolean checkIsNeedCured() {
			float duration = now() - startTime;
			if (duration <= minDuration) {
				return false;
			}

			// +3 回合: Luck -40
			// +4 回合: Luck -20
			// +5 回合: Luck +0
			// +6 回合: Luck +20
			// ...
			float checkRate = checkRateFunction.apply(duration);
			return createRollToCheckCure((int) checkRate);
		}

		public DurationBuffSpan withVStat(VStat vStat) {
			this.vStat = vStat;
			return this;
		}

		public DurationBuffSpan withStartTime(float startTime) {
			this.startTime = startTime;
			return this;
		}

		public DurationBuffSpan withMinDuration(float duration) {
			this.minDuration = duration;
			return this;
		}

		public DurationBuffSpan withCheckRateFunction(Function<Float, Float> checkRateFunction) {
			this.checkRateFunction = checkRateFunction;
			return this;
		}
	}

	/**
	 * 适用于触发固定次数后慢慢治愈或消失的 Buff.
	 */
	public class CountBuffSpan extends BuffSpan {
		int count;
		int minCount = 3;
		Function<Integer, Integer> checkRateFunction = duration -> duration * 20 - 100;

		@Override
		public boolean checkIsNeedCured() {
			count++;
			if (count <= minCount) {
				return false;
			}

			// +3 回合: Luck -40
			// +4 回合: Luck -20
			// +5 回合: Luck +0
			// +6 回合: Luck +20
			// ...
			int checkRate = checkRateFunction.apply(count);
			return createRollToCheckCure(checkRate);
		}

		public CountBuffSpan withVStat(VStat vStat) {
			this.vStat = vStat;
			return this;
		}

		public CountBuffSpan withMinCount(int minCount) {
			this.minCount = minCount;
			return this;
		}

		public CountBuffSpan withCheckRateFunction(Function<Integer, Integer> checkRateFunction) {
			this.checkRateFunction = checkRateFunction;
			return this;
		}
	}

	public abstract class BuffSpan {
		protected VStat vStat = VStat.LUCK;

		public boolean createRollToCheckCure(int checkRate) {
			final Roll roll = target.createRoll(vStat, checkRate);
			pushMessage(new RollReportMessage(target, new Roll[]{roll})
					.withTitle(getCategory()));

			if (roll.result) {
				cure();
			}

			return roll.result;
		}

		public void cure() {
			target.removeBuff(FoeStageBuff.this);
			pushMessage(new FoeRemoveBuffMessage(FoeStageBuff.this));
		}

		public boolean checkIsNeedCured(Hooks.HookContext context) {
			return this.checkIsNeedCured();
		}

		/**
		 * @return 返回 true 时说明 buff 已经痊愈 (被删除)
		 */
		public abstract boolean checkIsNeedCured();
	}
}
